# -*- coding: utf-8 -*-

################################################################################
## Form generated from reading UI file 'audio_to_text.ui'
##
## Created by: Qt User Interface Compiler version 5.15.2
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
import os
import sys
import traceback
from PySide2.QtCore import *
from PySide2.QtGui import *
from PySide2.QtWidgets import *
import logging
import time
from datetime import datetime
import zhconv
# from faster_whisper_audio_tool import FasterWhisperModel
from tools.common_util import check_exits_audio_or_video_file, AUDIO_OR_VIDEO_SUFFIX, VIDEO_SUFFIX, video_to_audio, \
    write_to_text
from tools.faster_whisper_audio_tool import FasterWhisperModel

os.environ["HF_ENDPOINT"] = "https://hf-mirror.com"

BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
LOG_PATH = os.path.join(BASE_DIR, 'logs', 'output.log')

logging.basicConfig(
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    level=logging.INFO,
    datefmt='%Y-%m-%d %H:%M:%S',
    filename=LOG_PATH,
    filemode='a'
)

logger = logging.getLogger(__name__)


class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        if not MainWindow.objectName():
            MainWindow.setObjectName(u"MainWindow")
        MainWindow.resize(800, 600)
        self.centralwidget = QWidget(MainWindow)
        self.centralwidget.setObjectName(u"centralwidget")
        self.verticalLayout = QVBoxLayout(self.centralwidget)
        self.verticalLayout.setObjectName(u"verticalLayout")
        self.horizontalLayout_3 = QHBoxLayout()
        self.horizontalLayout_3.setObjectName(u"horizontalLayout_3")
        self.horizontalSpacer = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)

        self.horizontalLayout_3.addItem(self.horizontalSpacer)

        self.run_btn = QPushButton(self.centralwidget)
        self.run_btn.setObjectName(u"run_btn")

        self.horizontalLayout_3.addWidget(self.run_btn)

        self.verticalLayout.addLayout(self.horizontalLayout_3)

        self.horizontalLayout = QHBoxLayout()
        self.horizontalLayout.setObjectName(u"horizontalLayout")
        self.audio_label = QLabel(self.centralwidget)
        self.audio_label.setObjectName(u"audio_label")
        self.audio_label.setSizeIncrement(QSize(0, 0))

        self.horizontalLayout.addWidget(self.audio_label)

        self.audio_lineEdit = QLineEdit(self.centralwidget)
        self.audio_lineEdit.setObjectName(u"audio_lineEdit")

        self.horizontalLayout.addWidget(self.audio_lineEdit)

        self.select_audio_btn = QPushButton(self.centralwidget)
        self.select_audio_btn.setObjectName(u"select_audio_btn")

        self.horizontalLayout.addWidget(self.select_audio_btn)

        self.verticalLayout.addLayout(self.horizontalLayout)

        self.horizontalLayout_2 = QHBoxLayout()
        self.horizontalLayout_2.setObjectName(u"horizontalLayout_2")
        self.output_label = QLabel(self.centralwidget)
        self.output_label.setObjectName(u"output_label")

        self.horizontalLayout_2.addWidget(self.output_label)

        self.output_lineEdit = QLineEdit(self.centralwidget)
        self.output_lineEdit.setObjectName(u"output_lineEdit")

        self.horizontalLayout_2.addWidget(self.output_lineEdit)

        self.output_btn = QPushButton(self.centralwidget)
        self.output_btn.setObjectName(u"output_btn")

        self.horizontalLayout_2.addWidget(self.output_btn)

        self.verticalLayout.addLayout(self.horizontalLayout_2)

        self.horizontalLayout_4 = QHBoxLayout()
        self.horizontalLayout_4.setObjectName(u"horizontalLayout_4")
        self.only_output_audio_radioButton = QRadioButton(self.centralwidget)
        self.only_output_audio_radioButton.setObjectName(u"only_output_audio_radioButton")

        self.horizontalLayout_4.addWidget(self.only_output_audio_radioButton)

        self.horizontalSpacer_2 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)

        self.horizontalLayout_4.addItem(self.horizontalSpacer_2)

        self.device_label = QLabel(self.centralwidget)
        self.device_label.setObjectName(u"device_label")

        self.horizontalLayout_4.addWidget(self.device_label)

        self.device_comboBox = QComboBox(self.centralwidget)
        self.device_comboBox.addItem("")
        self.device_comboBox.addItem("")
        self.device_comboBox.setObjectName(u"device_comboBox")

        self.horizontalLayout_4.addWidget(self.device_comboBox)

        self.horizontalSpacer_4 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)

        self.horizontalLayout_4.addItem(self.horizontalSpacer_4)

        self.language_label = QLabel(self.centralwidget)
        self.language_label.setObjectName(u"language_label")

        self.horizontalLayout_4.addWidget(self.language_label)

        self.language_comboBox = QComboBox(self.centralwidget)
        self.language_comboBox.addItem("")
        self.language_comboBox.addItem("")
        self.language_comboBox.setObjectName(u"language_comboBox")

        self.horizontalLayout_4.addWidget(self.language_comboBox)

        self.horizontalSpacer_5 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)

        self.horizontalLayout_4.addItem(self.horizontalSpacer_5)

        self.verticalLayout.addLayout(self.horizontalLayout_4)

        self.horizontalLayout_5 = QHBoxLayout()
        self.horizontalLayout_5.setObjectName(u"horizontalLayout_5")
        self.local_model_comboBox = QComboBox(self.centralwidget)
        self.local_model_comboBox.addItem("")
        self.local_model_comboBox.addItem("")
        self.local_model_comboBox.setObjectName(u"local_model_comboBox")

        self.horizontalLayout_5.addWidget(self.local_model_comboBox)

        self.local_model_lineEdit = QLineEdit(self.centralwidget)
        self.local_model_lineEdit.setObjectName(u"local_model_lineEdit")

        self.horizontalLayout_5.addWidget(self.local_model_lineEdit)

        self.select_local_model_btn = QPushButton(self.centralwidget)
        self.select_local_model_btn.setObjectName(u"select_local_model_btn")

        self.horizontalLayout_5.addWidget(self.select_local_model_btn)

        self.verticalLayout.addLayout(self.horizontalLayout_5)

        self.horizontalLayout_7 = QHBoxLayout()
        self.horizontalLayout_7.setObjectName(u"horizontalLayout_7")
        self.model_label = QLabel(self.centralwidget)
        self.model_label.setObjectName(u"model_label")

        self.horizontalLayout_7.addWidget(self.model_label)

        self.model_comboBox = QComboBox(self.centralwidget)
        self.model_comboBox.addItem("")
        self.model_comboBox.addItem("")
        self.model_comboBox.addItem("")
        self.model_comboBox.setObjectName(u"model_comboBox")

        self.horizontalLayout_7.addWidget(self.model_comboBox)

        self.horizontalSpacer_3 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)

        self.horizontalLayout_7.addItem(self.horizontalSpacer_3)

        self.verticalLayout.addLayout(self.horizontalLayout_7)

        self.horizontalLayout_6 = QHBoxLayout()
        self.horizontalLayout_6.setObjectName(u"horizontalLayout_6")
        self.cache_model_btn = QPushButton(self.centralwidget)
        self.cache_model_btn.setObjectName(u"cache_model_btn")

        self.horizontalLayout_6.addWidget(self.cache_model_btn)

        self.cache_model_lineEdit = QLineEdit(self.centralwidget)
        self.cache_model_lineEdit.setObjectName(u"cache_model_lineEdit")

        self.horizontalLayout_6.addWidget(self.cache_model_lineEdit)

        self.model_download_btn = QPushButton(self.centralwidget)
        self.model_download_btn.setObjectName(u"model_download_btn")

        self.horizontalLayout_6.addWidget(self.model_download_btn)

        self.verticalLayout.addLayout(self.horizontalLayout_6)

        self.tableWidget = QTableWidget(self.centralwidget)
        if (self.tableWidget.columnCount() < 4):
            self.tableWidget.setColumnCount(4)
        __qtablewidgetitem = QTableWidgetItem()
        self.tableWidget.setHorizontalHeaderItem(0, __qtablewidgetitem)
        __qtablewidgetitem1 = QTableWidgetItem()
        self.tableWidget.setHorizontalHeaderItem(1, __qtablewidgetitem1)
        __qtablewidgetitem2 = QTableWidgetItem()
        self.tableWidget.setHorizontalHeaderItem(2, __qtablewidgetitem2)
        __qtablewidgetitem3 = QTableWidgetItem()
        self.tableWidget.setHorizontalHeaderItem(3, __qtablewidgetitem3)
        self.tableWidget.setObjectName(u"tableWidget")

        self.verticalLayout.addWidget(self.tableWidget)

        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QMenuBar(MainWindow)
        self.menubar.setObjectName(u"menubar")
        self.menubar.setGeometry(QRect(0, 0, 800, 23))
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QStatusBar(MainWindow)
        self.statusbar.setObjectName(u"statusbar")
        MainWindow.setStatusBar(self.statusbar)

        self.retranslateUi(MainWindow)

        QMetaObject.connectSlotsByName(MainWindow)

    # setupUi

    def retranslateUi(self, MainWindow):
        MainWindow.setWindowTitle(QCoreApplication.translate("MainWindow", u"MainWindow", None))
        self.run_btn.setText(QCoreApplication.translate("MainWindow", u"\u8fd0\u884c", None))
        self.audio_label.setText(QCoreApplication.translate("MainWindow", u"\u97f3/\u89c6\u9891\u6587\u4ef6", None))
        self.select_audio_btn.setText(QCoreApplication.translate("MainWindow", u"\u9009\u62e9\u6587\u4ef6\u5939", None))
        self.output_label.setText(QCoreApplication.translate("MainWindow", u"\u8f93\u51fa", None))
        self.output_btn.setText(QCoreApplication.translate("MainWindow", u"\u8f93\u51fa\u6587\u672c\u8def\u5f84", None))
        self.only_output_audio_radioButton.setText(
            QCoreApplication.translate("MainWindow", u"\u53ea\u8f93\u51fa\u97f3\u9891", None))
        self.device_label.setText(QCoreApplication.translate("MainWindow", u"\u5904\u7406\u8bbe\u5907\uff1a", None))
        self.device_comboBox.setItemText(0, QCoreApplication.translate("MainWindow", u"CPU", None))
        self.device_comboBox.setItemText(1, QCoreApplication.translate("MainWindow", u"GPU", None))

        self.language_label.setText(QCoreApplication.translate("MainWindow", u"\u8bed\u8a00\uff1a", None))
        self.language_comboBox.setItemText(0, QCoreApplication.translate("MainWindow", u"zh", None))
        self.language_comboBox.setItemText(1, QCoreApplication.translate("MainWindow", u"en", None))

        self.local_model_comboBox.setItemText(0, QCoreApplication.translate("MainWindow",
                                                                            u"\u4f7f\u7528\u672c\u5730\u6a21\u578b",
                                                                            None))
        self.local_model_comboBox.setItemText(1, QCoreApplication.translate("MainWindow",
                                                                            u"\u5728\u7ebf\u4e0b\u8f7d\u6a21\u578b",
                                                                            None))

        self.local_model_lineEdit.setPlaceholderText(
            QCoreApplication.translate("MainWindow", u"\u672c\u5730\u6a21\u578b\u8def\u5f84", None))
        self.select_local_model_btn.setText(QCoreApplication.translate("MainWindow", u"\u9009\u62e9", None))
        self.model_label.setText(QCoreApplication.translate("MainWindow", u"\u6a21\u578b\u540d\u79f0", None))
        self.model_comboBox.setItemText(0, QCoreApplication.translate("MainWindow", u"tiny", None))
        self.model_comboBox.setItemText(1, QCoreApplication.translate("MainWindow", u"small", None))
        self.model_comboBox.setItemText(2, QCoreApplication.translate("MainWindow", u"base", None))

        self.cache_model_btn.setText(
            QCoreApplication.translate("MainWindow", u"\u6a21\u578b\u7f13\u5b58\u76ee\u5f55", None))
        self.model_download_btn.setText(QCoreApplication.translate("MainWindow", u"\u4e0b\u8f7d", None))
        ___qtablewidgetitem = self.tableWidget.horizontalHeaderItem(0)
        ___qtablewidgetitem.setText(QCoreApplication.translate("MainWindow", u"\u65f6\u95f4", None));
        ___qtablewidgetitem1 = self.tableWidget.horizontalHeaderItem(1)
        ___qtablewidgetitem1.setText(QCoreApplication.translate("MainWindow", u"\u8017\u65f6", None));
        ___qtablewidgetitem2 = self.tableWidget.horizontalHeaderItem(2)
        ___qtablewidgetitem2.setText(QCoreApplication.translate("MainWindow", u"\u72b6\u6001", None));
        ___qtablewidgetitem3 = self.tableWidget.horizontalHeaderItem(3)
        ___qtablewidgetitem3.setText(QCoreApplication.translate("MainWindow", u"\u8def\u5f84", None));
    # retranslateUi


class WorkerSignals(QObject):
    """
    定义信号槽，QRunnable 没有信号机制，只有 QObject 才有
    """
    error = Signal(object)
    result = Signal(object)
    progress = Signal(str)


class LocalTask(QRunnable):

    def __init__(self, audio_path, output_path, only_output_audio, select_device, local_model_path, select_language):
        super(LocalTask, self).__init__()

        self.audio_path = audio_path
        self.output_path = output_path
        self.only_output_audio = only_output_audio
        self.select_device = select_device
        self.local_model_path = local_model_path
        self.select_language = select_language

        self.signals = WorkerSignals()  # 信号
        self.faster_whisper_model = None

    def run(self):
        result = {
            "status": "success",
            "audio_path": self.audio_path,
            "cost_time": 0,
            "start_time": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        }

        st = time.perf_counter()

        try:
            if not self.only_output_audio:
                self.faster_whisper_model = FasterWhisperModel(
                    self.local_model_path,
                    device=self.select_device.lower(),
                    compute_type="int8",
                    local_files_only=True,
                    num_workers=5
                )

            # 转换
            for root, dirs, files in os.walk(self.audio_path):
                relative_path = os.path.relpath(root, self.audio_path)

                for file in files:
                    if not file.endswith(AUDIO_OR_VIDEO_SUFFIX):
                        continue

                    file_path = os.path.join(root, file)
                    file_name = os.path.splitext(file)[0]

                    output_subpath = os.path.join(self.output_path, relative_path)
                    os.makedirs(output_subpath, exist_ok=True)
                    logger.info(f"output_subpath: {output_subpath}")

                    # 若是视频文件，提取音频
                    if file.endswith(VIDEO_SUFFIX):
                        extract_audio_path = os.path.join(output_subpath, f"{file_name}.wav")

                        # 提取音频
                        audio_path = video_to_audio(file_path, extract_audio_path)
                    else:
                        audio_path = file_path

                    # 仅提取音频
                    if self.only_output_audio:
                        continue

                    output_audio_path = os.path.join(output_subpath, f"{file_name}.txt")
                    logger.info(f"output_audio_path: {output_audio_path}\tfile_path: {file_path}")

                    # 音频转文字
                    segments, info = self.faster_whisper_model.transcribe(audio_path, language=self.select_language,
                                                                          beam_size=5)
                    content = []
                    for segment in segments:
                        text = segment.text
                        print("[%.2fs -> %.2fs] %s" % (segment.start, segment.end, text))

                        # 繁体转简体
                        if self.select_language == "zh":
                            text = zhconv.convert(text, "zh-cn")

                        string = f"[{segment.start:.2f} -> {segment.end:.2f}] {text}"
                        content.append(string)

                    write_to_text(output_audio_path, content)

        except Exception as e:
            print(traceback.format_exc())
            result["status"] = "failed"

            self.signals.error.emit(str(traceback.format_exc()))

        result["cost_time"] = f"{time.perf_counter() - st:.2f}s"

        self.signals.result.emit(result)  # Return the result of the processing


class OnLineTask(QRunnable):

    def __init__(self, audio_path, output_path, only_output_audio, select_device, cache_model_path, select_model,
                 select_language):
        super(OnLineTask, self).__init__()

        self.audio_path = audio_path
        self.output_path = output_path
        self.only_output_audio = only_output_audio
        self.select_device = select_device
        self.cache_model_path = cache_model_path
        self.select_model = select_model
        self.select_language = select_language

        self.signals = WorkerSignals()  # 信号
        self.faster_whisper_model = None

    # @Slot
    def run(self):
        result = {
            "status": "success",
            "audio_path": self.audio_path,
            "cost_time": 0,
            "start_time": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        }

        st = time.perf_counter()

        try:
            if not self.only_output_audio:
                self.faster_whisper_model = FasterWhisperModel(
                    self.select_model,
                    device=self.select_device.lower(),
                    compute_type="int8",
                    local_files_only=False,
                    num_workers=5,
                    download_root=self.cache_model_path
                )

            # 转换
            for root, dirs, files in os.walk(self.audio_path):
                relative_path = os.path.relpath(root, self.audio_path)

                for file in files:
                    if not file.endswith(AUDIO_OR_VIDEO_SUFFIX):
                        continue

                    file_path = os.path.join(root, file)
                    file_name = os.path.splitext(file)[0]

                    output_subpath = os.path.join(self.output_path, relative_path)
                    os.makedirs(output_subpath, exist_ok=True)
                    logger.info(f"output_subpath: {output_subpath}")

                    # 若是视频文件，提取音频
                    if file.endswith(VIDEO_SUFFIX):
                        extract_audio_path = os.path.join(output_subpath, f"{file_name}.wav")

                        # 提取音频
                        audio_path = video_to_audio(file_path, extract_audio_path)
                    else:
                        audio_path = file_path

                    # 仅提取音频
                    if self.only_output_audio:
                        continue

                    output_audio_path = os.path.join(output_subpath, f"{file_name}.txt")
                    logger.info(f"output_audio_path: {output_audio_path}\tfile_path: {file_path}")

                    # 音频转文字
                    segments, info = self.faster_whisper_model.transcribe(audio_path, language=self.select_language,
                                                                          beam_size=5)
                    content = []
                    for segment in segments:
                        text = segment.text
                        print("[%.2fs -> %.2fs] %s" % (segment.start, segment.end, text))

                        # 繁体转简体
                        if self.select_language == "zh":
                            text = zhconv.convert(text, "zh-cn")

                        string = f"[{segment.start:.2f} -> {segment.end:.2f}] {text}"
                        content.append(string)

                    write_to_text(output_audio_path, content)

        except Exception as e:
            print(traceback.format_exc())
            result["status"] = "failed"

            self.signals.error.emit(str(traceback.format_exc()))

        result["cost_time"] = f"{time.perf_counter() - st:.2f}s"

        self.signals.result.emit(result)  # Return the result of the processing


class AudioToText(QMainWindow, Ui_MainWindow):
    def __init__(self):
        super().__init__()
        # self.setFixedSize(800, 600)
        self.setFixedSize(1200, 800)

        self.setupUi(self)
        self.setWindowTitle("语音转文字工具v1.1")
        self.init_signal()
        self.init_ui()

        self.select_audio_path = None
        self.select_output_path = None
        self.select_local_model_path = None
        self.cache_model_path = None  # 模型缓存路径

        self.threadpool = QThreadPool()  # 线程池

    def init_ui(self):
        self.model_label.hide()
        self.model_comboBox.hide()
        self.cache_model_btn.hide()
        self.cache_model_lineEdit.hide()
        self.model_download_btn.hide()

        # init default value
        # self.audio_lineEdit.setText(r"D:\WorkSpace\version_controller\audio-to-text\sources\input")
        # self.output_lineEdit.setText(r"D:\WorkSpace\version_controller\audio-to-text\sources\output")
        # self.local_model_lineEdit.setText(
        #     r"D:\WorkSpace\version_controller\audio-to-text\sources\models\faster-whisper\base")

        self.tableWidget.setHorizontalHeaderLabels(['时间', '耗时', '状态', '路径'])

    def init_signal(self):
        self.local_model_comboBox.activated.connect(self.on_combobox_activated)
        self.run_btn.clicked.connect(self.on_run_btn_clicked)
        self.select_audio_btn.clicked.connect(self.select_input_folder)
        self.output_btn.clicked.connect(self.select_output_folder)
        self.select_local_model_btn.clicked.connect(self.select_local_model_folder)
        self.model_download_btn.clicked.connect(self.handle_online_model_download)
        self.cache_model_btn.clicked.connect(self.select_cache_model_folder)

    def handle_online_model_download(self):
        """
        在线下载模型
        :return:
        """
        # 获取输入框的值
        cache_model_path = self.cache_model_lineEdit.text().strip()

        # 校验输入框是否为空
        if not cache_model_path:
            QMessageBox.warning(self, "警告", "模型缓存路径不能为空")
            return

        # 校验模型缓存路径是否存在
        if not os.path.exists(cache_model_path):
            QMessageBox.warning(self, "警告", "模型缓存路径不存在")
            return

        # 下拉框选择的模型
        select_model = self.model_comboBox.currentText()
        logger.info(f"select_model: {select_model}")

        # 调用运行方法
        self.run_task()

    def select_cache_model_folder(self):
        """
        在线选择下载模型缓存路径
        :return:
        """
        folder = QFileDialog.getExistingDirectory(self, "选择输入文件夹")
        self.cache_model_path = folder.strip()
        self.cache_model_lineEdit.setText(self.cache_model_path)

    def select_input_folder(self):
        folder = QFileDialog.getExistingDirectory(self, "选择输入文件夹")
        self.select_audio_path = folder.strip()
        self.audio_lineEdit.setText(self.select_audio_path)

    def select_output_folder(self):
        folder = QFileDialog.getExistingDirectory(self, "选择输出文件夹")
        self.select_output_path = folder.strip()
        self.output_lineEdit.setText(self.select_output_path)

    def select_local_model_folder(self):
        folder = QFileDialog.getExistingDirectory(self, "选择输出文件夹")
        self.select_local_model_path = folder.strip()
        self.local_model_lineEdit.setText(self.select_local_model_path)

    def on_combobox_activated(self, index):
        selected_text = self.local_model_comboBox.itemText(index)
        logger.info(f"选择了：{selected_text}")

        if selected_text == "使用本地模型":
            self.model_label.hide()
            self.model_comboBox.hide()
            self.cache_model_btn.hide()
            self.cache_model_lineEdit.hide()
            self.model_download_btn.hide()

            # 显示本地模型相关
            self.local_model_lineEdit.setEnabled(True)
            self.select_local_model_btn.setEnabled(True)
        else:
            # 在线模型
            self.model_label.show()
            self.model_comboBox.show()
            self.cache_model_btn.show()
            self.cache_model_lineEdit.show()
            self.model_download_btn.show()

            # 隐藏本地模型相关
            self.local_model_lineEdit.setEnabled(False)
            self.select_local_model_btn.setEnabled(False)

    def table_widget_add_row(self, row, columns):
        """
        QTableWidget 添加一行
        :param row:
        :param columns:
        :return:
        """
        self.tableWidget.insertRow(row)

        for i, column in enumerate(columns):
            self.tableWidget.setItem(row, i, QTableWidgetItem(column))

        # 自适应列宽度
        self.tableWidget.resizeColumnsToContents()

    def table_widget_update_row(self, row, col, value):
        """
        QTableWidget 更新行
        :param row:
        :param col,:
        :param value:
        :return:
        """
        self.tableWidget.setItem(row, col, QTableWidgetItem(value))

        # 自适应列宽度
        self.tableWidget.resizeColumnsToContents()

    def handle_local_model(self, audio_path, output_path, only_output_audio, select_device, local_model_path,
                           select_language):
        """
        使用本地模型
        :param audio_path:
        :param output_path:
        :param only_output_audio:
        :param select_device:
        :param local_model_path:
        :param select_language:
        :return:
        """
        task = LocalTask(audio_path, output_path, only_output_audio, select_device, local_model_path, select_language)
        task.signals.result.connect(self.handle_local_result)
        task.signals.error.connect(self.handle_local_error)
        logger.info(f"开始执行多线程: {task}")

        # QTableWidget 添加一行
        row = self.tableWidget.rowCount()
        start_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        self.table_widget_add_row(row, [start_time, "", "padding", audio_path])

        self.threadpool.start(task)  # 执行

    def handle_local_result(self, result):
        """
        子线程结果
        result = {
            "status": "success",
            "audio_path": self.audio_path,
            "cost_time": 0,
            "start_time": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        }
        :param result:
        :return:
        """
        # if result["status"] == "success":
        #     QMessageBox.information(self, "提示", "转换成功")
        # else:
        #     QMessageBox.warning(self, "警告", "转换失败")

        row = self.tableWidget.rowCount() - 1
        logger.info(f"result: {result}\rrow: {row}")

        self.table_widget_update_row(row, 1, result["cost_time"])
        self.table_widget_update_row(row, 2, result["status"])

    def handle_local_error(self, error_msg):
        """
        子线程异常结果
        :param error_msg:
        :return:
        """
        logger.info(f"error_msg: {error_msg}")
        QMessageBox.warning(self, "警告", error_msg)

    def handle_online_model(self, audio_path, output_path, only_output_audio, select_device, cache_model_path,
                            select_model, select_language):
        """
        在线下载模型
        :param audio_path: 输入音频或视频
        :param output_path: 输出路径
        :param only_output_audio: 是否仅输出音频
        :param select_device: GPU或CPU
        :param cache_model_path: 缓存的模型目录
        :param select_model: 选择的模型名称
        :param select_language: 选择的语言
        :return:
        """
        task = OnLineTask(audio_path, output_path, only_output_audio, select_device, cache_model_path, select_model,
                          select_language)
        task.signals.result.connect(self.handle_local_result)
        task.signals.error.connect(self.handle_local_error)
        logger.info(f"开始执行多线程: {task}")

        # QTableWidget 添加一行
        row = self.tableWidget.rowCount()
        start_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        self.table_widget_add_row(row, [start_time, "", "padding", audio_path])

        self.threadpool.start(task)  # 执行

    def run_task(self):
        # 校验输入框是否为空
        audio_path = self.audio_lineEdit.text().strip()
        output_path = self.output_lineEdit.text().strip()
        if not audio_path:
            QMessageBox.warning(self, "警告", "音频文件路径不能为空")
            return

        if not output_path:
            QMessageBox.warning(self, "警告", "输出路径不能为空")
            return

        # 仅输出音频
        only_output_audio = self.only_output_audio_radioButton.isChecked()

        # 处理设备下拉框
        select_device = self.device_comboBox.currentText()

        # 校验输入路径中是否音频或视频文件
        is_exists_audio_or_video = check_exits_audio_or_video_file(audio_path)
        if not is_exists_audio_or_video:
            QMessageBox.warning(self, "警告", "音频或视频文件不存在")
            return

        # 语言下拉框
        select_language = self.language_comboBox.currentText()

        # 模型下拉框
        select_model_comboBox = self.local_model_comboBox.currentText()
        try:
            if select_model_comboBox == "使用本地模型":
                local_model_path = self.local_model_lineEdit.text().strip()

                if not local_model_path:
                    QMessageBox.warning(self, "警告", "本地模型路径不能为空")
                    return

                # 校验模型是否存在
                if not os.path.exists(local_model_path):
                    QMessageBox.warning(self, "警告", "本地模型路径不存在")
                    return

                logger.info(f"local_model_path: {local_model_path}")
                self.run_btn.setEnabled(False)

                self.handle_local_model(audio_path, output_path, only_output_audio, select_device, local_model_path,
                                        select_language)
            else:
                # 获取输入框的值
                cache_model_path = self.cache_model_lineEdit.text().strip()

                # 下拉框选择的模型
                select_model = self.model_comboBox.currentText()

                self.run_btn.setEnabled(False)
                self.handle_online_model(audio_path, output_path, only_output_audio, select_device, cache_model_path,
                                         select_model, select_language)
        except Exception as e:
            logger.error(traceback.format_exc())
            QMessageBox.warning(self, "警告", f"异常：{e}")
        finally:
            self.run_btn.setEnabled(True)

    def on_run_btn_clicked(self):
        self.run_task()


if __name__ == "__main__":
    app = QApplication(sys.argv)
    # 创建一个字体对象，并设置字体大小为 12
    font = QFont()
    font.setPointSize(16)

    # 设置应用程序的字体
    app.setFont(font)

    window = AudioToText()
    window.show()
    sys.exit(app.exec_())
